﻿using NUnit.Framework;
using ValidationFramework.Tests.Performance.TestHelpers;
using ValidationFramework.Reflection;

namespace ValidationFramework.Tests.Performance
{
	[TestFixture]
	public class ValidationPolicyTests
	{
		#region Test Model

		public class ClassToValidate
		{
			public int PropertyToValidate
			{
				get;
				set;
			}
		}

		#endregion

		[Test]
		public void Validate_With_One_Member_Rule()
		{
			// Arrange
			var typeHandle = typeof(ClassToValidate).TypeHandle;

			// Arrange | Setup Descriptor
			var descriptor = new FakeInfoDescriptor(typeHandle, "PropertyToValidate", null);

			// Arrange | Setup Memeber Rules
			var memberRules = new RuleCollection(typeHandle) {{new Rule(new AlwaysFailValidator())}};

		    // Arrange | Construct Policy
			var policy = new ValidationPolicy(descriptor);
			policy.AddRules(memberRules, RuleScope.Member);

			// Assert Preconditions
			Assert.AreEqual(0, policy.ParentScopeRules.Count);
			Assert.AreEqual(1, policy.MemberScopeRules.Count);
			Assert.AreEqual(1, policy.Validate("SomeValue", new ValidationOptions()).Count);

			// Act
			var options = new ValidationOptions();
            const int iterations = 1000000;
			using (var stopWatch = new DisposableStopwatch("ValidationPolicy.Validate Performance Test", iterations))
			{
				stopWatch.Run(() => policy.Validate("SomeValue", options));
			}
		}
		[Test]
		public void Validate_With_One_Parent_Rule()
		{
			// Arrange
			var typeHandle = typeof(ClassToValidate).TypeHandle;

			// Arrange | Setup Descriptor
            var descriptor = new FakeInfoDescriptor(typeHandle, "PropertyToValidate", null);

			// Arrange | Setup Parent Rules
			var parentRules = new RuleCollection(typeHandle);
			parentRules.Add(new Rule(new AlwaysFailValidator()));

			// Arrange | Construct Policy
			var policy = new ValidationPolicy(descriptor);
			policy.AddRules(parentRules, RuleScope.Parent);

			// Assert Preconditions
			Assert.AreEqual(1, policy.ParentScopeRules.Count);
			Assert.AreEqual(0, policy.MemberScopeRules.Count);
			Assert.AreEqual(1, policy.Validate("SomeValue", new ValidationOptions()).Count);

			// Act
			var options = new ValidationOptions();
			const int iterations = 10000;
			using (var stopWatch = new DisposableStopwatch("ValidationPolicy.Validate Performance Test", iterations))
			{
				stopWatch.Run(() => policy.Validate("SomeValue", options));
			}
		}
		[Test]
		public void Validate_With_Many_Rules()
		{
			// Arrange
			var typeHandle = typeof(ClassToValidate).TypeHandle;

			// Arrange | Setup Descriptor
            var descriptor = new FakeInfoDescriptor(typeHandle, "PropertyToValidate", null);

			// Arrange | Setup Parent Rules
			const int numberOfParentRules = 5;
			var parentRules = new RuleCollection(typeHandle);
			for (var i = 0; i < numberOfParentRules; i++)
			{
				parentRules.Add(new Rule(new AlwaysFailValidator()));
			}

			// Arrange | Setup Memeber Rules
			const int numberOfMemberRules = 5;
			var memberRules = new RuleCollection(typeHandle);
			for (var i = 0; i < numberOfMemberRules; i++)
			{
				memberRules.Add(new Rule(new AlwaysFailValidator()));
			}

			// Arrange | Construct Policy
			var policy = new ValidationPolicy(descriptor);
			policy.AddRules(parentRules, RuleScope.Parent);
			policy.AddRules(memberRules, RuleScope.Member);

			
			// Assert Preconditions
			Assert.AreEqual(numberOfParentRules, policy.ParentScopeRules.Count);
			Assert.AreEqual(numberOfMemberRules, policy.MemberScopeRules.Count);
			Assert.AreEqual(policy.RuleCount, policy.Validate("SomeValue", new ValidationOptions()).Count);

			// Act
			var options = new ValidationOptions();
			const int iterations = 10000;
			using (var stopWatch = new DisposableStopwatch("ValidationPolicy.Validate Performance Test", iterations))
			{
				stopWatch.Run(() => policy.Validate("SomeValue", options));
			}
		}
		[Test]
		public void ValidateNoParent_With_One_Memeber_Rule()
		{
			// Arrange
			var typeHandle = typeof(ClassToValidate).TypeHandle;

			// Arrange | Setup Descriptor
            var descriptor = new FakeInfoDescriptor(typeHandle, "PropertyToValidate", null);

			// Arrange | Setup Memeber Rules
			var memberRules = new RuleCollection(typeHandle);
			memberRules.Add(new Rule(new AlwaysFailValidator()));

			// Arrange | Construct Policy
			var policy = new ValidationPolicy(descriptor);
			policy.AddRules(memberRules, RuleScope.Member);


			// Assert Preconditions
			Assert.AreEqual(0, policy.ParentScopeRules.Count);
			Assert.AreEqual(1, policy.MemberScopeRules.Count);
			Assert.AreEqual(policy.RuleCount, policy.Validate("SomeValue", new ValidationOptions()).Count);

			// Act
			var options = new ValidationOptions();
			const int iterations = 10000;
			using (var stopWatch = new DisposableStopwatch("ValidationPolicy.Validate Performance Test", iterations))
			{
				stopWatch.Run(() => policy.ValidateNoParent("SomeValue", options));
			}

		}
		[Test]
		public void ValidateNoParent_With_Many_Member_Rules()
		{
			// Arrange
			var typeHandle = typeof(ClassToValidate).TypeHandle;

			// Arrange | Setup Descriptor
            var descriptor = new FakeInfoDescriptor(typeHandle, "PropertyToValidate", null);

			// Arrange | Setup Memeber Rules
			const int numberOfMemberRules = 5;
			var memberRules = new RuleCollection(typeHandle);
			for (var i = 0; i < numberOfMemberRules; i++)
			{
				memberRules.Add(new Rule(new AlwaysFailValidator()));
			}

			// Arrange | Construct Policy
			var policy = new ValidationPolicy(descriptor);
			policy.AddRules(memberRules, RuleScope.Member);

			// Assert Preconditions
			Assert.AreEqual(0, policy.ParentScopeRules.Count);
			Assert.AreEqual(numberOfMemberRules, policy.MemberScopeRules.Count);
			Assert.AreEqual(policy.RuleCount, policy.Validate("SomeValue", new ValidationOptions()).Count);

			// Act
			var options = new ValidationOptions();
			const int iterations = 10000;
			using (var stopWatch = new DisposableStopwatch("ValidationPolicy.Validate Performance Test", iterations))
			{
				stopWatch.Run(() => policy.ValidateNoParent("SomeValue", options));
			}

		}
	}
}
